//
//  Request.swift
//  NetWork
//
//  Created by Delin Meng 孟德林 on 2018/7/30.
//  Copyright © 2018年 Delin Meng 孟德林. All rights reserved.
//

import UIKit

// 请求适配器
public protocol RequestAdapter {
    
    func adapt(_ urlRequest:URLRequest) throws -> URLRequest
    
}

public typealias RequestRetryCompletion = (_ shouldRetry:Bool,_ timerDelay:TimeInterval) -> Void

// 请求重试
public protocol RequestRetrier {
    
    func should(_ manager:SessionManager,retry request:Request,with error:Error,completion:  RequestRetryCompletion)
    
}

// 任务转换
public protocol TaskConvertible {
    
    func task(session:URLSession,adapter:RequestAdapter?,queue:DispatchQueue) throws -> URLSessionTask
    
}


public typealias HTTPHeaders = [String:String]


///  请求的基本类

open class Request {

    // 进度回调
    public typealias ProgressHandler = (Progress) -> Void
    
    // 任务枚举
    enum RequestTask {
        case data(TaskConvertible?,URLSessionTask?)
        case download(TaskConvertible?,URLSessionTask?)
        case upload(TaskConvertible?,URLSessionTask?)
        case stream(TaskConvertible?,URLSessionTask?)
    }
    
    // 任务代理
    open internal(set) var delegate:TaskDelegate {
        get {
            taskDelegateLock.lock() ;defer { taskDelegateLock.unlock()}
            return taskDelegate
        }
        set {
            taskDelegateLock.lock() ; defer {  taskDelegateLock.unlock() }
            taskDelegate = newValue
        }
    }
    
    // 任务锁
    private var taskDelegate:TaskDelegate
    private var taskDelegateLock = NSLock()
    
    
    open var task:URLSessionTask?
    
    open var session:URLSession
    /// 请求属性，从 URLSessionTask.originalRequest 获取到
    open var request:URLRequest? { return task?.originalRequest }
    /// 响应属性，从 URLSessionTask.response
    open var response:HTTPURLResponse? { return task?.response as? HTTPURLResponse }
    
    // 重复次数 (模块中可以 set ，模块外外部调用只能 get)
    let originalTask: TaskConvertible? // 任务转换
    
    var startTime:CFAbsoluteTime?
    
    var endTime:CFAbsoluteTime?
    
    // 验证回调的数组
    var validations:[()->Void] = []

    
    init(session: URLSession, requestTask:RequestTask,error:Error? = nil) {
        self.session = session
        switch requestTask {
        case .data(let orginalTask, let task):
            taskDelegate = TaskDelegate(task: task)
            self.originalTask = orginalTask
        case .download(let orginalTask, let task):
            taskDelegate = TaskDelegate(task: task)
            self.originalTask = orginalTask
        case .upload(let orginalTask, let task):
            taskDelegate = TaskDelegate(task: task)
            self.originalTask = orginalTask
        case .stream(let orginalTask, let task):
            taskDelegate = TaskDelegate(task: task)
            self.originalTask = orginalTask
        }
    }

    
    //
    //    public enum Persistence : UInt {
    //
    //
    //        case none       // 没有任何操作
    //
    //        case forSession // 仅仅为了当前会话
    //
    //        case permanent  // 永久性
    //
    //        @available(iOS 6.0, *)
    //        case synchronizable // 同步性
    //    }
    
    
    @discardableResult
    open func authenticte(user:String,password:String,persistence:URLCredential.Persistence = .forSession) -> Self {
        
        let credential = URLCredential(user: user, password: password, persistence: persistence)
        return authenticate(usingCredential: credential)
    }
    
    @discardableResult
    open func authenticate(usingCredential credential:URLCredential) -> Self {
        
        // 将证书交给代理处理
        return self
    }
    
    public static func authorizationHeader(user:String,password:String) -> (key:String,value:String)? {
        
        guard let data = "\(user):\(password)".data(using: .utf8) else {
            return nil
        }
        let credential = data.base64EncodedData(options: [])
        return (key:"Authorization",value:"Basic\(credential)")
    }
    
    
    /// 任务开始  （Suspended 暂停）
    
    open func resume() {
        
        guard let task = task else {
            // 代理的队列暂停属性设置为 false
            return
        }
        
        if startTime == nil { startTime = CFAbsoluteTimeGetCurrent() }
        task.resume()
        
        /// 任务开始
        NotificationCenter.default.post(name: Notification.Name.Task.DidResume, object: self, userInfo: [NSNotification.Key.Task : task])
    }
    
    /// 任务暂停
    open func suspend() {
        
        guard let task = task else { return }
        task.suspend()
        NotificationCenter.default.post(name: Notification.Name.Task.DidSuspend, object: self, userInfo: [NSNotification.Key.Task : task])
    }
    

    /// 取消任务
    
    open func cancel() {
        
        guard let task = task else { return }
        task.cancel()
        
        NotificationCenter.default.post(name: Notification.Name.Task.DidCancel, object: self, userInfo: [NSNotification.Key.Task : task])

    }
    
}

/// 实现字符串协议
extension Request:CustomStringConvertible {
    public var description: String {
        var components:[String] = []
        if let HTTPMethod = request?.httpMethod {
            components.append(HTTPMethod)
        }
        
        if let urlString = request?.url?.absoluteString {
            components.append(urlString)
        }
        
        if let response = response {
            components.append("(\(response.statusCode))")
        }
        return components.joined(separator: " ")
    }
    
}


/// 实现字符串 debug 协议

extension Request:CustomDebugStringConvertible {
    public var debugDescription: String {
        return cURLRepresentation()
    }
    func cURLRepresentation() -> String {
        var components = ["$ cur1 -v"]
        guard let request = self.request,
              let url = request.url,
              let host = url.host
            else{
                return "$ curl command could not be created"
        }
        
        if let httpMethod = request.httpMethod, httpMethod != "GET" {
            components.append("-X \(httpMethod)")
        }
        
        /// 证书
        if let credentialStorage =
            self.session.configuration.urlCredentialStorage{
            let protectionSpace = URLProtectionSpace(host: host, port: url.port ?? 0, protocol: url.scheme, realm: host, authenticationMethod: NSURLAuthenticationMethodHTTPBasic)
            // protectionSpace 保护空间
            
            if let credentials = credentialStorage.credentials(for: protectionSpace)?.values {
                for credential in credentials {
                    guard let user = credential.user, let password = credential.password else { continue }
                    components.append("-u \(user):\(password)")
                }
            }else { // 获取代理的证书
                
                
            }
        }
        
        /// cookies
        if session.configuration.httpShouldSetCookies {
            if let cookieStorage = session.configuration.httpCookieStorage,
                let cookies = cookieStorage.cookies(for: url),!cookies.isEmpty {
                
                let string = cookies.reduce("") { $0 + "\($1.name)=\($1.value);"}
                #if swift(>=3.2)
                components.append("-b \"\(string[..<string.index(before: string.endIndex)])\"")
                #else
                components.append("-b \"\(string.substring(to: string.characters.index(before: string.endIndex)))\"")
                #endif
            }
        }
        
        /// 请求头 headers
        var headers:[AnyHashable:Any] = [:]
        if let additionalHeaders = session.configuration.httpAdditionalHeaders {
            for (field,value) in additionalHeaders where field != AnyHashable("Coolie"){
                headers[field] = value
            }
        }
    
        if let headerFields = request.allHTTPHeaderFields {
            
            for (field,value) in headerFields where field != "Cookie" {
                headers[field] = value
            }
        }
        
        for (field,value) in headers {
            components.append("-H \"\(field):\(value)\"")
        }
        
        if let httpBodyData = request.httpBody, let httpBody = String(data: httpBodyData, encoding: .utf8) {
            var escapedBody = httpBody.replacingOccurrences(of: "\\\"", with: "\\\\\"")
            escapedBody = escapedBody.replacingOccurrences(of: "\"", with: "\\\"")
            
            components.append("-d \"\(escapedBody)\"")
        }
        
        components.append("\"\(url.absoluteString)\"")
        
        return components.joined(separator: " \\\n\t")
        
    }
    
}


/// 普通的数据请求

open class DataRequest:Request {
    
    /// 可请求的
    struct Requestable:TaskConvertible {
        let urlRequest : URLRequest
        
        func task(session: URLSession, adapter: RequestAdapter?, queue: DispatchQueue) throws -> URLSessionTask {
            do {
                let urlRequest = try self.urlRequest.adapt(using: adapter)
                return queue.sync{      // 同步创建任务
                    session.dataTask(with: urlRequest)
                }
            } catch  {
                throw error
            }
        }
    }
    
    
    /// 请求发送或者被发送到
    
    open override var request: URLRequest? {
        if let request = super.request { return request }
        if let requestable = originalTask as? Requestable {
           return requestable.urlRequest
        }
        return nil
    }
    
//    open var progress: Progress{ return dataDelegate }
    
    var dataDelegate:DataTaskDelegate { return delegate as! DataTaskDelegate}
    
    @discardableResult  // closure 结束
    open func stream(closure:((Data) -> Void)? = nil) -> Self {
//        dataDelegate.dataStream = closure
        return self
    }
    
    @discardableResult
    open func downloadProgress(queue: DispatchQueue = DispatchQueue.main,closure:ProgressHandler) -> Self {
//        dataDelegate.progressHandler = (closure,queue)
        return self
    }
    
    
}


/// 数据下载

open class DownloadRequest:Request {
    
    /// 下载的类型
    public struct DownloadOptions:OptionSet {
        
        public let rawValue:UInt
        
        public static let createIntermediateDirectories = DownloadOptions(rawValue: 1 << 0)
        
        public static let removePreviousFile = DownloadOptions(rawValue: 1 << 1)
        
        public init(rawValue:UInt) {
            self.rawValue = rawValue
        }
    }
    
    /// 下载的回调信心
    public typealias DownloadFileDestination = (
        _ temporaryURL:URL,
        _ response:HTTPURLResponse) -> (destinationURL:URL,options:DownloadOptions)
    
    /// 下载的枚举类型
    enum Downloadable:TaskConvertible {
        case request(URLRequest)
        case resumeData(Data)
        
        func task(session: URLSession, adapter: RequestAdapter?, queue: DispatchQueue) throws -> URLSessionTask {
         
            do {
                let task:URLSessionTask
                switch self {
                case let .request(urlrequest):
                    let urlrequest = try urlrequest.adapt(using: adapter)
                    task = queue.sync {
                        session.dataTask(with: urlrequest)
                    }
                case let .resumeData(resumeData):
                    task = queue.sync {
                        session.downloadTask(withResumeData: resumeData)
                    }
                }
                return task
            } catch  {
                throw error
            }
        }
    }

    open override var request: URLRequest? {
        
        if let request = super.request { return request }
        if let downloadable = originalTask as? Downloadable,case let .request(urlRequest) = downloadable {
            return urlRequest
        }
        return nil
    }
    
    open var resumeData:Data? { return downloadDelegate.resumeData }

    open var progress:Progress { return downloadDelegate.progress }

    var downloadDelegate:DownloadTaskDelegate { return delegate as! DownloadTaskDelegate }
    
    open override func cancel() {
        
        downloadDelegate.downloadTask.cancel { self.downloadDelegate.resumeData = $0 }
        NotificationCenter.default.post(name: Notification.Name.Task.DidCancel, object: self, userInfo: [NSNotification.Key.Task: task as Any])
    }
    
    @discardableResult
    open func downloadProgress(queue:DispatchQueue = DispatchQueue.main,closure: @escaping ProgressHandler) -> Self {
        downloadDelegate.progressHandler = (closure,queue)
        return self
    }
    
    
    /// 建议下载目的地
    open class func suggestedDownloadDestination(for directory:FileManager.SearchPathDirectory = .documentDirectory,
                                                 in domain:FileManager.SearchPathDomainMask = .userDomainMask
        ) -> DownloadFileDestination {
        
        return { temporaryURL,response in
            let directoryURLs = FileManager.default.urls(for: directory, in: domain)
            if !directoryURLs.isEmpty {
                return (directoryURLs[0].appendingPathComponent(response.suggestedFilename!),[])
            }
            return (temporaryURL,[])
        }
    }
}



open class UploadRequest:DataRequest {


    enum Uploadable:TaskConvertible {

        case data(Data,URLRequest)
        case file(URL,URLRequest)
        case stream(InputStream,URLRequest)



        func task(session: URLSession, adapter: RequestAdapter?, queue: DispatchQueue) throws -> URLSessionTask {
            do {
                let task:URLSessionTask
                switch self {
                case let .data(data,urlRequest):
                    let urlRequest = try urlRequest.adapt(using: adapter)
                    task = queue.sync { session.uploadTask(with: urlRequest, from: data) }
                case let .file(url, urlRequest):
                    let urlRequest = try urlRequest.adapt(using: adapter)
                    task = queue.sync { session.uploadTask(with: urlRequest, fromFile: url) }
                case let .stream(_, urlRequest):
                    let urlRequest = try urlRequest.adapt(using: adapter)
                    task = queue.sync { session.uploadTask(withStreamedRequest: urlRequest) }
                }
                return task
            }catch{
                fatalError("name")
            }
        }

    }



    open override var request: URLRequest? {

        if let request = super.request { return request }
        guard let uploadable = originalTask as? Uploadable else { return nil }
        switch uploadable {
        case .data(_, let urlRequest), .file(_, let urlRequest), .stream(_, let urlRequest):
            return urlRequest
        }

    }

    open var uploadProgress:Progress { return uploadDelegate.uploadProgress }

    var uploadDelegate: UploadTaskDelegate { return delegate as! UploadTaskDelegate }

    @discardableResult
    open func uploadProgress(queue:DispatchQueue = DispatchQueue.main,closure: @escaping ProgressHandler) -> Self {
        uploadDelegate.uploadProgressHandler = (closure,queue)
        return self
    }

}


// MARK: -

#if !os(watchOS)

/// Specific type of `Request` that manages an underlying `URLSessionStreamTask`.
@available(iOS 9.0, macOS 10.11, tvOS 9.0, *)
open class StreamRequest: Request {
    enum Streamable: TaskConvertible {
        case stream(hostName: String, port: Int)
        case netService(NetService)

        func task(session: URLSession, adapter: RequestAdapter?, queue: DispatchQueue) throws -> URLSessionTask {
            let task: URLSessionTask

            switch self {
            case let .stream(hostName, port):
                task = queue.sync { session.streamTask(withHostName: hostName, port: port) }
            case let .netService(netService):
                task = queue.sync { session.streamTask(with: netService) }
            }

            return task
        }
    }
}

#endif
